﻿using Apewer.Internals;
using Externals.Compression.Checksums;
using Externals.Compression.Zip;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;

namespace Apewer
{

    /// <summary>二进制。</summary>
    public static class BytesUtility
    {

        /// <summary>空字节数组，每次获取都将创建新的引用。</summary>
        public static byte[] Empty = new byte[0];

        /// <summary>默认缓冲区大小。</summary>
        public const int DefaultBuffer = 4096;

        // 预置十六进制字符。
        private static readonly char[] UpperHex = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        private static readonly char[] LowerHex = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

        #region ASCII

        /// <summary>CRLF</summary>
        public static byte[] CRLF { get => new byte[] { 13, 10 }; }

        /// <summary>CR</summary>
        public static byte[] CR { get => new byte[] { 13 }; }

        /// <summary>LF</summary>
        public static byte[] LF { get => new byte[] { 10 }; }

        #endregion

        #region Bytes Array

        /// <summary>克隆字节数组。当源为 NULL 时获取零元素字节数组。</summary>
        public static byte[] Clone(this byte[] bytes)
        {
            if (bytes == null) return Empty;
            var length = bytes.Length;
            if (length < 1) return Empty;
            var result = new byte[length];
            Buffer.BlockCopy(bytes, 0, result, 0, length);
            return result;
        }

        /// <summary>每个字节取反。</summary>
        /// <remarks>value = 255 - value</remarks>
        public static byte[] Adverse(this byte[] bytes)
        {
            if (bytes == null || bytes.LongLength < 1L) return Empty;
            var adverse = new byte[bytes.LongLength];
            for (var i = 0L; i < bytes.LongLength; i++) adverse[i] = Convert.ToByte(255 - bytes[i]);
            return adverse;
        }

        /// <summary>确定此字节数组实例的开头是否与指定的字节数组匹配。</summary>
        public static bool StartsWith(this byte[] bytes, params byte[] head)
        {
            // 头部为空，视为匹配。
            if (head == null) return true;
            var length = head.Length;
            if (length < 1) return true;

            // 样本为空，视为不匹配。
            if (bytes == null) return false;
            if (bytes.Length < length) return false;

            // 对比头部字节。
            for (var i = 0; i < length; i++)
            {
                if (bytes[i] != head[i]) return false;
            }
            return true;
        }

        /// <summary>确定此字节数组实例的结尾是否与指定的字节数组匹配。</summary>
        public static bool EndsWith(this byte[] bytes, params byte[] foot)
        {
            // 尾部为空，视为匹配。
            if (foot == null) return true;
            var length = foot.Length;
            if (length < 1) return true;

            // 样本为空，视为不匹配。
            if (bytes == null) return false;
            if (bytes.Length < length) return false;

            // 对比尾部字节。
            var offset = bytes.Length - length;
            for (var i = 0; i < length; i++)
            {
                if (bytes[offset + i] != foot[i]) return false;
            }
            return true;
        }

        /// <summary>合并字节数组。</summary>
        public static byte[] Merge(IEnumerable<byte[]> array)
        {
            if (array == null) return Empty;

            var total = 0L;
            foreach (var bytes in array)
            {
                if (bytes == null) continue;
                total += bytes.LongLength;
            }
            var result = new byte[total];
            var offset = 0L;
            if (total > 0)
            {
                foreach (var bytes in array)
                {
                    if (bytes == null) continue;
                    var length = bytes.LongLength;
                    if (length < 1L) continue;
                    Array.Copy(bytes, 0L, result, offset, length);
                    offset += length;
                }
            }
            return result;
        }

        /// <summary>合并字节数组。</summary>
        public static byte[] Merge(params byte[][] array) => Merge(array as IEnumerable<byte[]>);

        /// <summary>为字节数组增加字节。</summary>
        public static byte[] Append(byte[] head, params byte[] bytes) => Merge(head, bytes);

        /// <summary>为文本数据添加 BOM 字节，若已存在则忽略。</summary>
        public static byte[] AddTextBom(this byte[] bytes)
        {
            var bom = new byte[] { 0xEF, 0xBB, 0xBF };
            if (bytes == null || bytes.LongLength < 1L) return bom;

            var hasBom = (bytes.Length >= 3) && (bytes[0] == 0xEF) && (bytes[1] == 0xBB) && (bytes[2] == 0xBF);
            return hasBom ? Merge(bytes) : Merge(bom, bytes);
        }

        /// <summary>去除文本数据的 BOM 字节，若不存在则忽略。</summary>
        public static byte[] WipeTextBom(this byte[] bytes)
        {
            if (bytes == null) return Empty;
            var hasBom = (bytes.Length >= 3) && (bytes[0] == 0xEF) && (bytes[1] == 0xBB) && (bytes[2] == 0xBF);
            var offset = hasBom ? 3 : 0;
            var length = bytes.Length - offset;
            var wiped = new byte[length];
            if (length > 0) Array.Copy(bytes, offset, wiped, 0, length);
            return wiped;
        }

        /// <summary>生成新的 GUID 数据。</summary>
        public static byte[] NewGuid() => Guid.NewGuid().ToByteArray();

        #endregion

        #region Text

        /// <summary>将字节数组转换为十六进制文本。</summary>
        public static string ToHex(this byte[] bytes)
        {
            int length = bytes.Length;
            if (length > 0)
            {
                var sb = new StringBuilder();
                for (int i = 0; i < length; i++)
                {
                    sb.Append(Constant.HexCollection[bytes[i] / 16]);
                    sb.Append(Constant.HexCollection[bytes[i] % 16]);
                }
                return sb.ToString();
            }
            return "";
        }

        /// <summary>将十六进制文本转换为字节数组。</summary>
        public static byte[] FromHex(this string hex)
        {
            if (string.IsNullOrEmpty(hex) || hex.Length < 2) return Empty;
            if (hex.Length % 2 != 0) return Empty;

            var lower = hex.ToLower().ToCharArray();
            var half = lower.Length / 2;
            var bytes = new byte[half];
            for (var i = 0; i < half; i++)
            {
                var offset = i * 2;
                var h = Constant.HexCollection.IndexOf(lower[offset]);
                var l = Constant.HexCollection.IndexOf(lower[offset + 1]);
                if (h < 0 || l < 0) return Empty;
                bytes[i] = Convert.ToByte((h * 16) + l);
            }

            return bytes;
        }

        /// <summary>将字节数组格式化为十六进制字符串，可指定大小写。</summary>
        /// <remarks>例：D41D8CD98F00B204E9800998ECF8427E</remarks>
        public static string ToX2(this byte[] bytes, bool upper = true)
        {
            if (bytes == null) return "";
            var length = bytes.Length;
            if (length < 1) return TextUtility.Empty;

            var hex = upper ? UpperHex : LowerHex;
            var chars = new char[length * 2];
            for (var i = 0; i < length; i++)
            {
                var b = bytes[i];
                var offset = i * 2;
                chars[offset] = hex[b / 16];
                chars[offset + 1] = hex[b % 16];
            }
            return new string(chars);
        }

        /// <summary>Byte[] -> Base64</summary>
        public static string ToBase64(params byte[] bytes)
        {
            if (bytes == null || bytes.Length < 1) return Constant.EmptyString;
            try { return Convert.ToBase64String(bytes); }
            catch { return Constant.EmptyString; }
        }

        /// <summary>Base64 -> Byte[]</summary>
        public static byte[] FromBase64(string base64)
        {
            if (string.IsNullOrEmpty(base64)) return Empty;
            try { return Convert.FromBase64String(base64); }
            catch { return Empty; }
        }

        /// <summary>转换字节数组为文本，默认使用 UTF-8 代码页。</summary>
        public static string ToText(byte[] bytes, Encoding encoding = null)
        {
            if (bytes.Length < 1) return Constant.EmptyString;
            try { return (encoding ?? Encoding.UTF8).GetString(bytes); }
            catch { return Constant.EmptyString; }
        }

        /// <summary>转换文本为字节数组，默认使用 UTF-8 代码页。</summary>
        public static byte[] FromText(string text, Encoding encoding = null)
        {
            if (string.IsNullOrEmpty(text)) return Empty;
            try { return (encoding ?? Encoding.UTF8).GetBytes(text); }
            catch { return Empty; }
        }

        #endregion

        #region 压缩、解压。

        /// <summary>对数据进行 GZip 压缩。</summary>
        public static byte[] ToGzip(byte[] plain)
        {
            if (plain == null || plain.Length == 0) return Empty;
            byte[] result;
            using (var output = new MemoryStream())
            {
                using (var zip = new GZipStream(output, CompressionMode.Compress, true))
                {
                    zip.Write(plain, 0, plain.Length);
                }
                result = output.ToArray();
            }
            return result;
        }

        /// <summary>对数据进行 GZip 解压。</summary>
        public static byte[] FromGzip(byte[] gzip)
        {
            if (gzip == null || gzip.Length == 0) return Empty;
            byte[] result;
            using (var input = new MemoryStream(gzip))
            {
                input.Position = 0;
                using (var output = new MemoryStream())
                {
                    using (var zip = new GZipStream(input, CompressionMode.Decompress, true))
                    {
                        Read(zip, output, null, 8192);
                        result = output.ToArray();
                    }
                }
            }
            return result;
        }

        /// <summary>压缩字典为 ZIP 文件。</summary>
        /// <param name="files">由文件名和文件内容组成的字典。</param>
        /// <param name="target">要输出的 ZIP 流。</param>
        public static Exception ToZip(Dictionary<string, byte[]> files, Stream target)
        {
            var zip = null as ZipOutputStream;
            try
            {
                if (files == null) return new ArgumentNullException();
                if (target == null) return new ArgumentNullException();
                if (!target.CanWrite) return new NotSupportedException();

                zip = new ZipOutputStream(target);
                zip.SetLevel(1);

                foreach (var file in files)
                {
                    var crc = new Crc32();
                    crc.Reset();
                    crc.Update(file.Value);

                    var zipentry = new ZipEntry(file.Key);
                    zipentry.CompressionMethod = CompressionMethod.Deflated;
                    //vzipentry.Size = vfile.Value.LongLength;
                    zipentry.Crc = crc.Value;
                    zipentry.IsUnicodeText = true;

                    zip.PutNextEntry(zipentry);
                    zip.Write(file.Value, 0, file.Value.Length);
                    zip.CloseEntry();
                }

                zip.IsStreamOwner = false;
                zip.Finish();
                zip.Flush();
                zip.Close();

                return null;
            }
            catch (Exception ex)
            {
                RuntimeUtility.Dispose(zip);
                return ex;
            }
        }

        /// <summary>压缩到 ZIP 流。</summary>
        /// <param name="names">ZIP 内的文件名。</param>
        /// <param name="target">要输出的 ZIP 流。</param>
        /// <param name="inputGetter">按名称获取文件的输入流。</param>
        /// <param name="modifildGetter">按名称获取文件的修改时间。</param>
        /// <param name="disposeFiles">释放已写入 ZIP 的输入流。</param>
        public static Exception ToZip(IEnumerable<string> names, Stream target, Func<string, Stream> inputGetter, Func<string, DateTime> modifildGetter = null, bool disposeFiles = false)
        {
            var zip = null as ZipOutputStream;
            try
            {
                if (names == null) return new ArgumentNullException("names");
                if (target == null) return new ArgumentNullException("target");
                if (!target.CanWrite) return new ArgumentNullException("target");

                zip = new ZipOutputStream(target);
                zip.SetLevel(1);

                foreach (var name in names)
                {
                    if (string.IsNullOrEmpty(name)) continue;

                    // stream
                    var capacity = 1024;
                    var input = inputGetter == null ? null : inputGetter(name);
                    if (input != null)
                    {
                        if (!input.CanSeek || !input.CanRead)
                        {
                            if (disposeFiles) RuntimeUtility.Dispose(input);
                            return new NotSupportedException("获取到的输入流不支持 Seek 或 Read。");
                        }
                    }

                    var crc = new Crc32();
                    crc.Reset();
                    if (input != null)
                    {
                        input.ResetPosition();
                        while (true)
                        {
                            var count = 0;
                            var buffer = new byte[capacity];
                            count = input.Read(buffer, 0, buffer.Length);
                            if (count == 0) break;
                            crc.Update(buffer, 0, count);
                        }
                    }

                    var entry = new ZipEntry(name);
                    entry.CompressionMethod = CompressionMethod.Deflated;
                    //vzipentry.Size = vfile.Value.LongLength;
                    entry.Crc = crc.Value;
                    entry.IsUnicodeText = true;
                    if (modifildGetter != null) entry.DateTime = modifildGetter(name);

                    zip.PutNextEntry(entry);
                    if (input != null)
                    {
                        input.ResetPosition();
                        while (true)
                        {
                            var count = 0;
                            var buffer = new byte[capacity];
                            count = input.Read(buffer, 0, buffer.Length);
                            if (count == 0) break;
                            zip.Write(buffer, 0, count);
                        }
                    }
                    zip.CloseEntry();

                    if (disposeFiles) RuntimeUtility.Dispose(input);
                }

                zip.IsStreamOwner = false;
                zip.Finish();
                zip.Flush();
                zip.Close();

                return null;
            }
            catch (Exception ex)
            {
                RuntimeUtility.Dispose(zip);
                return ex;
            }
        }

        /// <summary>压缩字典为 ZIP 包。</summary>
        /// <param name="files">由文件名和文件内容组成的字典。</param>
        public static byte[] ToZip(Dictionary<string, byte[]> files)
        {
            if (files == null) return null;

            var output = new MemoryStream();

            var input = null as Stream;
            var ex = ToZip(files.Keys, output, (name) =>
            {
                RuntimeUtility.Dispose(input);
                if (name.IsEmpty()) return null;

                var bytes = files[name];
                if (bytes == null || bytes.LongLength < 1L) return null;

                input = new MemoryStream();
                Write(input, bytes);
                return input;
            }, null, true);
            RuntimeUtility.Dispose(input);

            var result = output.ToArray();
            RuntimeUtility.Dispose(result);
            return result;
        }

        /// <summary>解压 ZIP 文件。</summary>
        public static Exception FromZip(Stream input, ZipOnFile onFile, ZipOnDirectory onDirectory = null, bool disposeOutput = false)
        {
            const int BufferCapacity = 1024;

            var zip = null as ZipInputStream;
            try
            {
                if (input == null) return new ArgumentNullException("input");
                if (!input.CanRead) return new NotSupportedException();
                if (onFile == null) return new ArgumentNullException("extraction");

                zip = new ZipInputStream(input);
                while (true)
                {
                    var entry = zip.GetNextEntry();
                    if (entry == null) break;

                    var name = entry.Name;
                    var size = entry.Size;
                    var modified = entry.DateTime;

                    if (entry.IsFile)
                    {
                        var output = null as Stream;
                        try
                        {
                            output = onFile(name, size, modified);
                            if (output == null) continue;
                            if (!output.CanWrite)
                            {
                                RuntimeUtility.Dispose(output);
                                continue;
                            }

                            var writed = 0L;
                            while (true)
                            {
                                var buffer = new byte[BufferCapacity];
                                var count = zip.Read(buffer, 0, BufferCapacity);
                                writed += count;
                                if (count < 1) break;
                                output.Write(buffer, 0, count);
                            }

                            if (disposeOutput) RuntimeUtility.Dispose(output, true);
                        }
                        catch (Exception ex)
                        {
                            if (disposeOutput) RuntimeUtility.Dispose(output, true);
                            RuntimeUtility.Dispose(zip);
                            return ex;
                        }
                    }

                    if (onDirectory != null && entry.IsDirectory)
                    {
                        try
                        {
                            onDirectory(name, modified);
                        }
                        catch (Exception ex)
                        {
                            RuntimeUtility.Dispose(zip);
                            return ex;
                        }
                    }
                }
                zip.Dispose();

                return null;
            }
            catch (Exception ex)
            {
                RuntimeUtility.Dispose(zip);
                return ex;
            }
        }

        /// <summary>解压 .ZIP 文件为字典。</summary>
        public static Dictionary<string, byte[]> FromZip(byte[] zip)
        {
            var result = new Dictionary<string, byte[]>();
            if (zip == null) return result;
            if (zip.LongLength < 1) return result;

            var packagememory = new System.IO.MemoryStream(zip);
            try
            {
                var zipstream = new ZipInputStream(packagememory);
                while (true)
                {
                    var entry = zipstream.GetNextEntry();
                    if (entry == null) break;

                    if (entry.IsFile)
                    {
                        var cellname = entry.Name;
                        var celldata = new byte[0];
                        {
                            var cellstream = new System.IO.MemoryStream();
                            while (true)
                            {
                                var blockdata = new byte[1024];
                                var blockread = zipstream.Read(blockdata, 0, 1024);
                                if (blockread < 1) break;
                                cellstream.Write(blockdata, 0, blockread);
                            }
                            celldata = cellstream.ToArray();
                            cellstream.Dispose();
                        }
                        if (result.ContainsKey(cellname)) result[cellname] = celldata;
                        else result.Add(cellname, celldata);
                    }
                }
                zipstream.Dispose();
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }
            packagememory.Dispose();
            return result;
        }

        #endregion

        #region Stream

        /// <summary>关闭，并释放流。</summary>
        public static void Dispose(Stream stream, bool flush = false, bool close = true)
        {
            if (stream != null)
            {
                try { if (flush) stream.Flush(); } catch { }
                try { if (close) stream.Close(); } catch { }
                try { stream.Dispose(); } catch { }
            }
        }

        /// <summary>关闭，并释放流。</summary>
        public static void Dispose(IEnumerable<Stream> streams, bool flush = false, bool close = true)
        {
            if (streams != null)
            {
                foreach (var stream in streams) Dispose(stream, flush, close);
            }
        }

        /// <summary>重置流的位置到开始位置。</summary>
        public static bool ResetPosition(Stream stream)
        {
            if (stream == null) return false;
            try
            {
                stream.Position = 0;
                if (stream.CanSeek) stream.Seek(0, SeekOrigin.Begin);
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>读取源流中的数据，并将数据写入目标流，获取写入的总字节数。</summary>
        /// <param name="source">要读取的源流。</param>
        /// <param name="destination">要写入的目标流。</param>
        /// <param name="progress">已写入的字节数，返回 TRUE 继续读取，返回 FALSE 中断读取。</param>
        /// <param name="buffer">缓冲区大小，最小值为 1。</param>
        /// <returns>已写入的字节数。</returns>
        public static long Read(Stream source, Stream destination, Func<long, bool> progress = null, int buffer = DefaultBuffer)
        {
            if (source == null || !source.CanRead) return 0;
            if (destination == null || !destination.CanWrite) return 0;

            var limit = buffer < 1 ? 1 : buffer;
            var total = 0L;
            var count = 0;
            var callback = progress == null ? false : true;
            while (true)
            {
                count = 0;
                var temp = new byte[limit];
                try
                {
                    count = source.Read(temp, 0, limit);
                    if (count < 1) break;
                }
                catch { break; }
                try
                {
                    destination.Write(temp, 0, count);
                }
                catch { break; }
                total += count;
                if (callback)
                {
                    var @continue = progress(total);
                    if (!@continue) break;
                }
            }
            return total;
        }

        /// <summary>读取源流中的数据，并将数据写入目标流，获取写入的总字节数。</summary>
        /// <param name="source">要读取的源流。</param>
        /// <param name="destination">要写入的目标流。</param>
        /// <param name="progress">已写入的字节数，返回 TRUE 继续读取，返回 FALSE 中断读取。</param>
        /// <param name="buffer">缓冲区大小，最小值为 1。</param>
        /// <returns>已写入的字节数。</returns>
        public static long Read(Stream source, Stream destination, Action<long> progress, int buffer = DefaultBuffer)
        {
            return Read(source, destination, (x) => { progress?.Invoke(x); return true; }, buffer);
        }

        /// <summary>读取源流中的数据。</summary>
        /// <param name="source">源流。</param>
        /// <param name="buffer">缓冲区大小，最小值为 1。</param>
        /// <param name="dispose">读取结束后释放源流。</param>
        public static byte[] Read(Stream source, int buffer = 4096, bool dispose = false)
        {
            var result = null as byte[];
            using (var memory = new MemoryStream())
            {
                Read(source, memory, null, buffer);
                result = memory.ToArray();
            }
            if (dispose) Dispose(source);
            return result;
        }

        /// <summary>读取源流中的数据。</summary>
        /// <param name="source">源流。</param>
        /// <param name="dispose">读取结束后释放源流。</param>
        public static byte[] Read(Stream source, bool dispose) => Read(source, DefaultBuffer, dispose);

        /// <summary>读取源流中的数据，并将数据写入目标流，获取写入的总字节数。</summary>
        /// <param name="sources">要读取的源流。</param>
        /// <param name="destination">要写入的目标流。</param>
        /// <param name="writed">已写入的字节数，返回 TRUE 继续读取，返回 FALSE 中断读取。</param>
        /// <param name="buffer">缓冲区大小，最小值为 1。</param>
        /// <returns>已写入的字节数。</returns>
        public static long Read(IEnumerable<Stream> sources, Stream destination, Func<long, bool> writed = null, int buffer = DefaultBuffer)
        {
            var total = 0L;
            if (sources != null)
            {
                if (writed == null)
                {
                    foreach (var source in sources) total += Read(source, destination, null, buffer);
                }
                else
                {
                    foreach (var source in sources)
                    {
                        Read(source, destination, (x) =>
                        {
                            total += x;
                            return writed(total);
                        }, buffer);
                    }
                }
            }
            return total;
        }

        /// <summary>读取源流中的数据，并将数据写入目标流，获取写入的总字节数。</summary>
        /// <param name="sources">要读取的源流。</param>
        /// <param name="destination">要写入的目标流。</param>
        /// <param name="writed">已写入的字节数，返回 TRUE 继续读取，返回 FALSE 中断读取。</param>
        /// <param name="buffer">缓冲区大小，最小值为 1。</param>
        /// <returns>已写入的字节数。</returns>
        public static long Read(IEnumerable<Stream> sources, Stream destination, Action<long> writed, int buffer = DefaultBuffer)
        {
            return Read(sources, destination, (x) => { writed?.Invoke(x); return true; }, buffer);
        }

        /// <summary>向目标流写入数据，最多可写入 2147483648 字节。</summary>
        public static int Write(Stream destination, byte[] bytes, Action<long> writed, int buffer = DefaultBuffer)
        {
            if (destination == null || !destination.CanWrite) return 0;
            if (bytes == null || bytes.Length < 1 || bytes.LongLength > int.MaxValue) return 0;

            var limit = buffer < 1 ? 1 : buffer;
            var total = 0;
            try
            {
                var length = bytes.Length;
                while (total < length)
                {
                    var block = length - total;
                    if (block > limit) block = limit;
                    destination.Write(bytes, total, block);
                    total += block;
                    writed?.Invoke(total);
                }
            }
            catch (Exception ex)
            {
                Logger.Internals.Exception(ex, $"{nameof(BytesUtility)}.{nameof(Write)}");
            }
            return total;
        }

        /// <summary>向目标流写入数据，最多可写入 2147483647 字节（> 20 GB）。</summary>
        public static int Write(Stream destination, params byte[] bytes) => Write(destination, bytes, null);

        #endregion

        #region PKCS #5

        /// <summary>使用密码生成密钥。</summary>
        static byte[] PKCS5(byte[] password, byte[] salt = null, int iterations = 1000, int bits = 32)
        {
            var rfc2898 = new Rfc2898DeriveBytes(password, salt, 1);
            return rfc2898.GetBytes(32);
        }

        #endregion

        #region AES

        private static void Aes256(byte[] key, byte[] salt, Func<RijndaelManaged, ICryptoTransform> create, Stream input, Stream output)
        {
            using (var rm = new RijndaelManaged())
            {
                rm.KeySize = 256;
                rm.BlockSize = 128;
                rm.Mode = CipherMode.ECB;
                rm.Padding = PaddingMode.PKCS7;

                var k = new Rfc2898DeriveBytes(SHA256(key), salt, 1);
                rm.Key = k.GetBytes(32);
                rm.IV = k.GetBytes(16);

                using (var ct = create.Invoke(rm))
                {
                    using (var cs = new CryptoStream(output, ct, CryptoStreamMode.Write))
                    {
                        Read(input, cs);
                        cs.Close();
                    }
                }
            }
        }

        /// <summary>执行 AES 加密。</summary>
        public static void Aes256Encrypt(Stream input, Stream output, byte[] key) => Aes256(key, key, rm => rm.CreateEncryptor(), input, output);

        /// <summary>执行 AES 解密。</summary>
        public static void Aes256Decrypt(Stream input, Stream output, byte[] key) => Aes256(key, key, rm => rm.CreateDecryptor(), input, output);

        private static RijndaelManaged Aes256Provider(byte[] key)
        {
            var k = key ?? Empty; // AesFill(key);
            var p = new RijndaelManaged();
            p.Key = k;
            p.Mode = CipherMode.ECB;
            p.Padding = PaddingMode.PKCS7;
            return p;
        }

        /// <summary>对数据进行 AES 加密。</summary>
        public static byte[] Aes256Encrypt(byte[] bytes, byte[] key = null)
        {
            if (bytes == null) return Empty;
            if (bytes.Length == 0) return Empty;
            var rm = Aes256Provider(key);
            var result = new byte[0];
            var ct = rm.CreateEncryptor();
            try
            {
                result = ct.TransformFinalBlock(bytes, 0, bytes.Length);
            }
            catch { }
            return result;
        }

        /// <summary>对数据进行 AES 解密。</summary>
        public static byte[] Aes256Decrypt(byte[] cipher, byte[] key = null)
        {
            if (cipher == null) return Empty;
            if (cipher.Length == 0) return Empty;
            var rm = Aes256Provider(key);
            var result = new byte[0];
            var ct = rm.CreateDecryptor();
            try
            {
                result = ct.TransformFinalBlock(cipher, 0, cipher.Length);
            }
            catch { }
            ct.Dispose();
            return result;
        }

        private static readonly byte[] AesDefaultIV = new byte[16];

        static T UseAes<T>(byte[] key, byte[] iv, CipherMode cipherMode, PaddingMode paddingMode, Func<RijndaelManaged, T> callback)
        {
            if (key == null) throw new ArgumentNullException(nameof(key));
            var keySize = key.Length * 8;
            switch (keySize)
            {
                case 128:
                case 192:
                case 256:
                    break;
                default:
                    throw new ArgumentException($"密钥大小【{keySize}】bits 无效。");
            }

            if (iv == null) iv = AesDefaultIV;
            var ivSize = iv.Length * 8;
            if (ivSize != 128) throw new ArgumentException($"初始化向量【{ivSize}】bits 无效。");

            using (var rijndael = new RijndaelManaged())
            {
                rijndael.Key = key;
                rijndael.IV = iv;
                rijndael.Mode = cipherMode;
                rijndael.Padding = paddingMode;

                return callback.Invoke(rijndael);
            }
        }

        static void UseAes(Stream input, Stream output, byte[] key, byte[] iv, CipherMode cipherMode, PaddingMode paddingMode, Func<RijndaelManaged, ICryptoTransform> create)
        {
            if (input == null) throw new ArgumentNullException(nameof(input));
            if (output == null) throw new ArgumentNullException(nameof(output));

            UseAes<object>(key, iv, cipherMode, paddingMode, rijndael =>
            {
                using (var transformer = create.Invoke(rijndael))
                {

                    using (var stream = new CryptoStream(output, transformer, CryptoStreamMode.Write))
                    {
                        Read(input, stream);
                        stream.Close();
                    }
                }
                return null;
            });
        }

        static byte[] UseAes(byte[] input, byte[] key, byte[] iv, CipherMode cipherMode, PaddingMode paddingMode, Func<RijndaelManaged, ICryptoTransform> create)
        {
            if (input == null) input = new byte[0];

            var result = null as byte[];
            return UseAes<byte[]>(key, iv, cipherMode, paddingMode, rijndael =>
            {
                using (var transformer = create.Invoke(rijndael))
                {
                    var result = transformer.TransformFinalBlock(input, 0, input.Length);
                    return result;
                }
            });
        }

        /// <summary>执行 AES 128/192/256 加密。</summary>
        /// <param name="plain">明文。</param>
        /// <param name="key">密钥。</param>
        /// <param name="cipherMode">块密码模式。</param>
        /// <param name="paddingMode">填充模式。</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException" />
        /// <exception cref="ArgumentException" />
        /// <exception cref="CryptographicException" />
        public static byte[] AesEncrypt(byte[] plain, byte[] key, CipherMode cipherMode = CipherMode.ECB, PaddingMode paddingMode = PaddingMode.PKCS7)
        {
            return UseAes(plain, key, null, cipherMode, paddingMode, (rijndael) => rijndael.CreateEncryptor(rijndael.Key, rijndael.IV));
        }

        /// <summary>执行 AES 128/192/256 加密。</summary>
        /// <param name="plain">明文。</param>
        /// <param name="key">密钥，长度必须是 128 位（16 字节）、192 位（24 字节）或 256 位（32 字节）。</param>
        /// <param name="iv">初始化向量，必须是 128 位（16 字节）。</param>
        /// <param name="cipherMode">块密码模式。</param>
        /// <param name="paddingMode">填充模式。</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException" />
        /// <exception cref="ArgumentException" />
        /// <exception cref="CryptographicException" />
        public static byte[] AesEncrypt(byte[] plain, byte[] key, byte[] iv, CipherMode cipherMode = CipherMode.ECB, PaddingMode paddingMode = PaddingMode.PKCS7)
        {
            return UseAes(plain, key, iv, cipherMode, paddingMode, (rijndael) => rijndael.CreateEncryptor(rijndael.Key, rijndael.IV));
        }

        /// <summary>执行 AES 128/192/256 解密。</summary>
        /// <param name="cipher">密文。</param>
        /// <param name="key">密钥，长度必须是 128 位（16 字节）、192 位（24 字节）或 256 位（32 字节）。</param>
        /// <param name="cipherMode">块密码模式。</param>
        /// <param name="paddingMode">填充模式。</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException" />
        /// <exception cref="ArgumentException" />
        /// <exception cref="CryptographicException" />
        public static byte[] AesDecrypt(byte[] cipher, byte[] key, CipherMode cipherMode = CipherMode.ECB, PaddingMode paddingMode = PaddingMode.PKCS7)
        {
            return UseAes(cipher, key, null, cipherMode, paddingMode, (rijndael) => rijndael.CreateEncryptor(rijndael.Key, rijndael.IV));
        }

        /// <summary>执行 AES 128/192/256 解密。</summary>
        /// <param name="cipher">密文。</param>
        /// <param name="key">密钥，长度必须是 128 位（16 字节）、192 位（24 字节）或 256 位（32 字节）。</param>
        /// <param name="iv">初始化向量，必须是 128 位（16 字节）。</param>
        /// <param name="cipherMode">块密码模式。</param>
        /// <param name="paddingMode">填充模式。</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException" />
        /// <exception cref="ArgumentException" />
        /// <exception cref="CryptographicException" />
        public static byte[] AesDecrypt(byte[] cipher, byte[] key, byte[] iv, CipherMode cipherMode = CipherMode.ECB, PaddingMode paddingMode = PaddingMode.PKCS7)
        {
            return UseAes(cipher, key, iv, cipherMode, paddingMode, (rijndael) => rijndael.CreateEncryptor(rijndael.Key, rijndael.IV));
        }

        #endregion

        #region Hash

        private static byte[] ComputeHash<T>(byte[] bytes) where T : HashAlgorithm, new()
        {
            if (bytes != null)
            {
                try
                {
                    using (var algorithm = new T())
                    {
                        var result = algorithm.ComputeHash(bytes);
                        algorithm.Clear();
                        return result;
                    }
                }
                catch { }
            }
            return Empty;
        }

        private static byte[] ComputeHash<T>(Stream stream, Action<long> progress) where T : HashAlgorithm, new()
        {
            if (progress == null)
            {

                using (var algorithm = new T())
                {
                    if (stream == null)
                    {
                        var result = algorithm.ComputeHash(Empty);
                        algorithm.Clear();
                        return result;
                    }
                    else
                    {
                        var result = algorithm.ComputeHash(stream);
                        algorithm.Clear();
                        return result;
                    }
                }
            }
            else
            {
                if (stream == null) return Empty;

                // 初始化。
                using (var algorithm = new T())
                {
                    algorithm.Initialize();

                    // 读取。
                    var count = 0;
                    var input = new byte[DefaultBuffer];
                    var output = new byte[DefaultBuffer];
                    while (true)
                    {
                        count = stream.Read(input, 0, DefaultBuffer);

                        if (count < DefaultBuffer)
                        {
                            algorithm.TransformFinalBlock(input, 0, count);
                            break;
                        }
                        else
                        {
                            algorithm.TransformBlock(input, 0, count, output, 0);
                        }
                    }

                    var result = algorithm.Hash;
                    algorithm.Clear();
                    return result;
                }
            }
        }

        /// <summary>获取 MD5 值。</summary>
        public static byte[] MD5(this byte[] bytes) => ComputeHash<MD5CryptoServiceProvider>(bytes);

        /// <summary>获取 MD5 值。</summary>
        public static byte[] MD5(this Stream stream, Action<long> progress = null) => ComputeHash<MD5CryptoServiceProvider>(stream, progress);

        /// <summary>获取 SHA1 值。</summary>
        public static byte[] SHA1(this byte[] bytes) => ComputeHash<SHA1CryptoServiceProvider>(bytes);

        /// <summary>获取 SHA1 值。</summary>
        public static byte[] SHA1(this Stream stream, Action<long> progress = null) => ComputeHash<SHA1CryptoServiceProvider>(stream, progress);

        /// <summary>获取 SHA256 值。</summary>
        public static byte[] SHA256(this byte[] bytes) => ComputeHash<SHA256CryptoServiceProvider>(bytes);

        /// <summary>获取 SHA256 值。</summary>
        public static byte[] SHA256(this Stream stream, Action<long> progress = null) => ComputeHash<SHA256CryptoServiceProvider>(stream, progress);

        /// <summary>获取 SHA512 值。</summary>
        public static byte[] SHA512(this byte[] bytes) => ComputeHash<SHA512CryptoServiceProvider>(bytes);

        /// <summary>获取 SHA512 值。</summary>
        public static byte[] SHA512(this Stream stream, Action<long> progress = null) => ComputeHash<SHA512CryptoServiceProvider>(stream, progress);

        #endregion

    }

}
